﻿#include "pch.h"
#if _QG_USE_GL
#include "qgdrv_gl_rdh.h"
#include "qgdrv_gl_cnv.h"
#include "qgp/qguimg.h"
#include "qg_blits.h"
#include "qg_math.h"

#define USE_DEPTH_AS_RENDER_BUFFER		1

//
void GLRdh::CheckActiveTexture(kint stage) throw()
{
#if 0
	if (QglGetMaxTextureImageUnits() <= 1)
	{
		_sion.tex_active = 0;
	}
	else
#endif
	{
		if (_sion.tex_active != stage)
		{
			QglActiveTexture(GL_TEXTURE0 + stage);

			_sion.tex_active = stage;
		}
	}
}

//
void GLRdh::CheckBindTexture(kint stage, GLenum type, GLuint id, GLint miplvl, GLint mipcnt) throw()
{
	GLTextureProperty& n = _sion.tex_props[stage];

	if (n.type == type && n.id == id)
		return;

	CheckActiveTexture(stage);

	if (n.type == type)
		glBindTexture(type, id);
	else
	{
		if (n.type != GL_NONE)
		{
			// 이전 바인드를 제거한다. glUnbindTexture 함수는 없으니깐.
			glBindTexture(n.type, 0);
		}

		if (type != GL_NONE)
		{
			// 새 바인드
			glBindTexture(type, id);
		}
	}

	if (QglSupportTextureParameter() && type != GL_NONE)
	{
		if (QglSupportTextureBaseLevel())
		{
			if (n.miplvl != miplvl)
			{
				glTexParameteri(type, GL_TEXTURE_BASE_LEVEL, miplvl < 0 ? 0 : miplvl);

				n.miplvl = miplvl;
			}
		}

		if (QglSupportTextureMaxLevel())
		{
			if (n.mipcnt != mipcnt)
			{
				glTexParameteri(type, GL_TEXTURE_MAX_LEVEL, miplvl < 0 ? mipcnt - 1 : miplvl);

				n.mipcnt = mipcnt;
			}
		}
	}
	else
	{
		n.miplvl = 0;
		n.mipcnt = 0;
	}

	n.type = type;
	n.id = id;
}

// 
void GLRdh::_CommitSplrState(kint stage, GLSplrState* splr, GLTexture* tex) throw()
{
	// 오픈GL은 텍스터 마다 샘플러 상태가 다르다는 거
	// -> 전역 설정 보관이 아니고 텍스쳐 마다 갖고 있을 것
	if (splr != NULL && splr != tex->_splr)
	{
		GLenum type = tex->_gl_type;
		GLSplrState::Info& ni = splr->_info;

		CheckActiveTexture(stage);

		if (!QglSupportTextureNPOT())
		{
			if (tex->_insts.adjpow2)
			{
				if (ni.wrap_s != GL_CLAMP_TO_EDGE)
					ni.wrap_s = GL_CLAMP_TO_EDGE;

				if (ni.wrap_t != GL_CLAMP_TO_EDGE)
					ni.wrap_t = GL_CLAMP_TO_EDGE;
			}
		}

		glTexParameteri(type, GL_TEXTURE_WRAP_S, ni.wrap_s);
		glTexParameteri(type, GL_TEXTURE_WRAP_T, ni.wrap_t);

		if (QglSupportTexture3D())
			glTexParameteri(type, GL_TEXTURE_WRAP_R, ni.wrap_r);

		if (QglSupportTextureLODBias())
			glTexParameteri(type, GL_TEXTURE_LOD_BIAS, ni.lod_bias);

		glTexParameteri(type, GL_TEXTURE_MIN_FILTER, GLConvert::MipFilter(ni.min_filter, tex->_insts.mips));
		glTexParameteri(type, GL_TEXTURE_MAG_FILTER, ni.mag_filter);

		// anisotropic

		if (QglSupportTextureCompare())
		{
			glTexParameteri(type, GL_TEXTURE_COMPARE_MODE, ni.cop_mode);
			glTexParameteri(type, GL_TEXTURE_COMPARE_FUNC, ni.cop_func);
		}

		//
		kobj_unload(tex->_splr);
		tex->_splr = splr;
		splr->Load();
	}
}

//
void GLRdh::_CommitTexture(kint stage, GLTexture* tex) throw()
{
	CheckBindTexture(stage,
		tex->_gl_type, (GLuint)tex->GetDescription(), 0, tex->_mips);
}

//
void GLRdh::_ProcessTextureStage(kint maxtexs) throw()
{
	for (kint i = 0; i < maxtexs; i++)
	{
		GLTexture* tex = _pend.tex_ptrs[i];

		if (false || tex == NULL)
		{
			// 헐퀴, 아직 안됨
			CheckBindTexture(i, GL_NONE, 0);
		}
		else
		{
			_CommitTexture(i, tex);
			_CommitSplrState(i, _pend.splr_ptrs[i], tex);
		}
	}

	for (kint i = maxtexs; i < QglGetMaxTextureImageUnits(); i++)
		CheckBindTexture(i, GL_NONE, 0);
}

//
qgResObj* GLRdh::_RoamInitTexture(const char* name, kint type, kint fmt, kuint width, kuint height, kuint depth, kuint mips, kint flags, qgImage* img)
{
	GLTexture* tex;

	if (K_OFLAG(flags, QGTEXF_OFF))
	{
		GLTextureOff* off = kobj_create(GLTextureOff);

		if (!off->_InitProperty(name, (qgTextureType)type, width, height, depth, mips, flags, (qgColorFormat)fmt, this) ||
			!off->_CreateTexture(NULL, this) ||
			!off->_CreateOff(this))
		{
			off->Unload();
			tex = NULL;
		}
		else
		{
			// 성공
			tex = off;
		}
	}
	else
	{
		tex = kobj_create(GLTexture);

		if (img == NULL)
		{
			if (!tex->_InitProperty(name, (qgTextureType)type, width, height, depth, mips, flags, (qgColorFormat)fmt, this) ||
				!tex->_CreateTexture(NULL, this))
			{
				tex->Unload();
				tex = NULL;
			}
		}
		else
		{
			if (!tex->_InitProperty(name, img, 0, flags, this) ||
				!tex->_CreateTexture(img, this))
			{
				tex->Unload();
				tex = NULL;
			}
		}
	}

	return tex;
}

//
qgResObj* GLRdh::_RoamInitMotionTexture(const char* filename, kint flags)
{
	GLTextureMotion* tex = kobj_create(GLTextureMotion);

	if (!tex->_CreateMotion(filename, flags, this))
	{
		tex->Unload();
		tex = NULL;
	}

	return tex;
}

//
qgResObj* GLRdh::_RoamInitDepthBuffer(kuint width, kuint height)
{
	bool usestencil = true;
	GLuint dsb;

#if USE_DEPTH_AS_RENDER_BUFFER
	// 렌더 버퍼 만들기
	glGenRenderbuffers(1, &dsb);

	if (dsb == 0)
		return NULL;

	glBindRenderbuffer(GL_RENDERBUFFER, dsb);

	// 프레임 버퍼에 깊이 버퍼 붙임
	if (usestencil)
	{
		glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height);

		if (glGetError() != GL_NO_ERROR)
		{
			// 스텐실이 문제면 포기
			glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
			usestencil = false;
		}
	}
	else
	{
		// 스텐실 안씀
		glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
		usestencil = false;
	}

	glBindRenderbuffer(GL_RENDERBUFFER, 0);
#else
	// 텍스쳐 버퍼 만들기
	glGenTextures(1, &dsb);
	CheckBindTexture(0, GL_TEXTURE_2D, dsb, 0, 0);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	// 프레임 버퍼에 깊이 버퍼 붙임
	if (usestencil)
	{
		glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_STENCIL, width, height, 0,
			GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL);

		if (glGetError() != GL_NO_ERROR)
		{
			glTexImage2D(GL_TEXTURE_2D, 0, QglGetDepthFormat(), width, height, 0,
				GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
			usestencil = false;
		}
	}
	else
	{
		glTexImage2D(GL_TEXTURE_2D, 0, QglGetDepthFormat(), width, height, 0,
			GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
		usestencil = false;
	}

	glBindTexture(GL_TEXTURE_2D, 0);
#endif

	//
	GLDepthBuffer* base = kobj_create(GLDepthBuffer);

	base->_node_desc = dsb;
	base->_width = width;
	base->_height = height;
	base->_stencil = usestencil;

	return base;
}


//////////////////////////////////////////////////////////////////////////
// 뎁스 버퍼
KIMPL_SUBCLASS(GLDepthBuffer, "GLDepthBuffer");

//
GLDepthBuffer::GLDepthBuffer() throw()
{

}

//
GLDepthBuffer::~GLDepthBuffer() throw()
{
	if (_node_desc != 0)
	{
		GLuint n = (GLuint)_node_desc;
#if USE_DEPTH_AS_RENDER_BUFFER
		glDeleteRenderbuffers(1, &n);
#else
		glDeleteTextures(1, &n);
#endif
	}
}

//
bool GLDepthBuffer::Attach(GLTextureOff* texoff)
{
	k_return_value_if_fail(texoff, false);

	GLuint fbo = (GLuint)texoff->GetDescription();
	GLuint dsb = (GLuint)_node_desc;

	glBindFramebuffer(GL_FRAMEBUFFER, fbo);
	//glDrawBuffer(GL_COLOR_ATTACHMENT0);

#if USE_DEPTH_AS_RENDER_BUFFER
	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, dsb);

	if (_stencil)
		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, dsb);
#else
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, dsb, 0);

	if (_stencil)
		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, dsb, 0);
#endif

	//
	const char* chk = GLTextureOff::CheckFBO();

	glBindFramebuffer(GL_FRAMEBUFFER, 0);

	if (chk != NULL)
	{
		kobj_tracet(KLV_ERROR, KERR_INVAL, chk);

		return false;
	}

	//
	_lastoff = texoff;	// 참조 안함

	return true;
}


//////////////////////////////////////////////////////////////////////////
// 텍스쳐
KIMPL_SUBCLASS(GLTexture, "GLTexture");

//
GLTexture::GLTexture() throw()
{
}

//
GLTexture::~GLTexture() throw()
{
	if (_node_desc)
	{
		GLuint n = (GLuint)_node_desc;
		glDeleteTextures(1, &n);
	}

	kobj_unload(_img_rsw);
	kobj_unload(_img_mip);

	kobj_unload(_splr);
}

//
bool GLTexture::_InitProperty(const char* name, qgImage* img, kuint mips, kint flags, GLRdh* rdh)
{
	kint width = img->GetWidth();
	kint height = img->GetHeight();
	kint depth = 1;
	qgColorFormat fmt = img->GetColorFormat();

	return _InitProperty(name, QGTEXTURE_2D, width, height, depth, mips, flags, fmt, rdh);
}

//
bool GLTexture::_InitProperty(const char* name, qgTextureType type, kuint width, kuint height, kuint depth, kuint mips, kint flags, qgColorFormat fmt, GLRdh* rdh)
{
	_key = name && *name ? k_strdup(name) : k_apsprintf("gl_texture_%Lu", k_nth());
	_type = type;
	_flags = flags;
	_mips = mips;
	_fmt = fmt;

	// 포맷 맞춤
	bool hasalpha;

	_gl_type = GLConvert::TextureType(_type);
	_gl_format = GLConvert::DetectColorFormat(_fmt, _pixel_format, _pixel_type, hasalpha);

	if (_gl_type == 0 || _gl_format == GL_NONE)
	{
		// 지원하지 않는 포맷
		return false;
	}

	_insts.alpha = hasalpha;

	// 여타 등등
	if (K_OFLAG(flags, QGRESF_DYNAMIC))
	{
		if (K_OFLAG(flags, QGTEXF_OFF))
		{
			// 다이나믹 + 오프 금지
			return false;
		}

		if (!K_OFLAG(flags, QGTEXF_NOMIP))
		{
			// 다이나믹 -> 밉맵 금지
			K_YFLAG(_flags, QGTEXF_NOMIP, true);
		}

		_mips = 1;

		// 오픈GL은 바로 매핑이 없기에 이미지 강제 처리
		K_YFLAG(_flags, QGTEXF_IMAGE, true);
	}
	else
	{
		if (K_OFLAG(flags, QGTEXF_OFF))
		{
			if (!K_OFLAG(flags, QGTEXF_NOMIP))
			{
				// 오프 -> 밉맵 금지
				K_YFLAG(_flags, QGTEXF_NOMIP, true);
			}

			_insts.off = true;
			_mips = 1;
		}
		else
		{
			if (K_OFLAG(flags, QGTEXF_NOMIP))
				_mips = 1;
			else
			{
				// 음... 밉맵처리... 몰러... 일단 자동 켬
				_insts.automip = true;
				_mips = 0;
			}
		}
	}

	// 최대 크기 맞춤
	const GLuint maxsize = qbOpenGL::QglGetTextureDimension();

	if (width < maxsize)
		_width = width;
	else
	{
		_width = maxsize;
		_insts.clamp = true;
	}

	if (height < maxsize)
		_height = height;
	else
	{
		_height = maxsize;
		_insts.clamp = true;
	}

	if (depth < maxsize)	// ES2에는 없지만 ES3부터 지원하니 일단 냅둠
		_depth = depth;
	else
	{
		_depth = maxsize;
		_insts.clamp = true;
	}

	// 정방 검사
	if (qbOpenGL::QglSupportTextureNPOT())
	{
		_gl_width = _width;
		_gl_height = _height;
		_gl_depth = _depth;
	}
	else
	{
		_gl_width = _math::OptimizeTextureSize(_width, true, 0);
		_gl_height = _math::OptimizeTextureSize(_height, true, 0);
		_gl_depth = _math::OptimizeTextureSize(_depth, true, 0);

		if (_gl_width != _width || _gl_height != _height)
			_insts.adjpow2 = true;
	}

	// 밉맵 개수
	if (_mips == 0)
		_mips = _math::GetMipmapLevels(_gl_width, _gl_height, _gl_depth);

	// 모션
	if (K_OFLAG(flags, QGTEXF_MOTION))
		_insts.motion = true;

	// 샘플러
	_splr = NULL;

	return true;
}

//
bool GLTexture::_CreateInternal(GLuint* ptex, GLRdh* rdh)
{
	// 텍스쳐 핸들 만들기
	glGenTextures(1, ptex);
	rdh->CheckBindTexture(0, _gl_type, *ptex, 0, _mips);

	// 기본 설정 때려주고
	if (_insts.off || _insts.adjpow2)
	{
		// FBO 또는 NPO2
		glTexParameteri(_gl_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(_gl_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

		if (qbOpenGL::QglSupportTexture3D())
			glTexParameteri(_gl_type, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
	}
	else
	{
		// 기냥
		glTexParameteri(_gl_type, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTexParameteri(_gl_type, GL_TEXTURE_WRAP_T, GL_REPEAT);

		if (qbOpenGL::QglSupportTexture3D())
			glTexParameteri(_gl_type, GL_TEXTURE_WRAP_R, GL_REPEAT);
	}

	if (_insts.automip)
	{
		// 자동 밉맵
		glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
		glTexParameteri(_gl_type, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameteri(_gl_type, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
	}
	else
	{
		glTexParameteri(_gl_type, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameteri(_gl_type, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	}

	if (qbOpenGL::QglSupportTextureBaseLevel())
		glTexParameteri(_gl_type, GL_TEXTURE_BASE_LEVEL, 0);

	if (qbOpenGL::QglSupportTextureMaxLevel())
		glTexParameteri(_gl_type, GL_TEXTURE_MAX_LEVEL, _mips - 1);

	// 텍스쳐 만들기
	switch (K_CAST_ENUM(_type))
	{
		case QGTEXTURE_1D:	// 1D도 2D로 처리함
		case QGTEXTURE_2D:
			glTexImage2D(GL_TEXTURE_2D, 0, _gl_format, _gl_width, _gl_height, 0, _pixel_format, _pixel_type, NULL);
			break;

		case QGTEXTURE_CUBE:
			for (kint f = 0; f < 6; f++)
				glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, 0, _gl_format, _gl_width, _gl_height, 0, _pixel_format, _pixel_type, NULL);
			break;
	}

	return true;
}

//
bool GLTexture::_CreateTexture(qgImage* img, GLRdh* rdh)
{
	GLuint tex;

	if (!img)
	{
		// 걍 텍스쳐 만들기
		if (!_CreateInternal(&tex, rdh))
			return false;
	}
	else
	{
		// 이미지 처리
		if (!_insts.adjpow2 && !_insts.clamp)
		{
			_img_rsw = img;
			img->Load();
		}
		else
		{
			_img_rsw = kobj_new(qgImage, _gl_width, _gl_height, _fmt);
			img->CopyToScale(_img_rsw);
		}

		if (!_CreateInternal(&tex, rdh))
			return false;

		_UploadTexture(true, 0, rdh);

		if (!K_OFLAG(_flags, QGTEXF_IMAGE))
			kobj_reset(&_img_rsw);
	}

	//
	_node_desc = (kuintptr)tex;

	return true;
}

//
void GLTexture::_UploadTexture(bool isnew, kint mipno, GLRdh* rdh)
{
	qgImage* img = mipno == 0 ? _img_rsw : _img_mip;
	k_return_if_fail(img);

	// 텍스쳐 이미지 처리
	if (!isnew)
	{
		rdh->CheckBindTexture(0, _gl_type, (GLuint)_node_desc, 0, _mips);

		glTexSubImage2D(GL_TEXTURE_2D, mipno, 0, 0,
			img->GetWidth(), img->GetHeight(), _pixel_format, _pixel_type, img->GetData());
	}
	else
	{
		// 처음 만들 때 설정을 넣는데, 여기 없어도 되는 루틴이긴 하지 않음?
		if (mipno == 0)
		{
			if (_insts.automip)
			{
				// 자동 밉맵
				glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
				glTexParameteri(_gl_type, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
				glTexParameteri(_gl_type, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
			}
			else
			{
				glTexParameteri(_gl_type, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
				glTexParameteri(_gl_type, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
			}
		}

		glTexImage2D(GL_TEXTURE_2D, mipno, _gl_format,
			img->GetWidth(), img->GetHeight(), 0, _pixel_format, _pixel_type, img->GetData());
	}

	// 밉맵 자동
	if (_insts.automip)
		qbOpenGL::QglGenerateMipmap(_gl_type);
}

//
bool GLTexture::Map(qbTextureMap* map, kint flag, kint mipno, kint sliceface)
{
	k_return_value_if_fail(map, false);
	k_return_value_if_fail(_lock_mip < 0, false);

	// PBO를 안써서... ㅠ_ㅜ

	// 잠글 수 있나
	GLenum pxfmt, pxtype;

	if (!GLConvert::TestLockableFormat(_fmt, pxfmt, pxtype))
		return false;

	//
	qgImage* img = _img_rsw;

	// 이미지가 없으면 만들기
	if (!img || _insts.off)
	{
		if (!img)
		{
			if (mipno == 0)
				img = _img_rsw = kobj_new(qgImage, _gl_width, _gl_height, _fmt);
			else
			{
				int i = 0, w = _gl_width, h = _gl_height;

				do
				{
					if (w > 1)
						w >>= 1;

					if (h > 1)
						h >>= 1;

					i++;
				} while (i != mipno);

				img = _img_mip = kobj_new(qgImage, w, h, _fmt);
			}
		}

		// 디바이스에서 이미지 다운로드
		if (K_OFLAG(flag, QGENTF_READ) && _gl_type == GL_TEXTURE_2D)
		{
			// glGetTexImage()가 없다.
			// FBO로 만들어서 glReadPixels()로 읽어야함
			// ...인데 귀찮아서 패스
		}
	}

	// 정리
	map->data = img->GetData();
	map->pitch = img->GetPitch();
	map->slice = 0;

	map->width = img->GetWidth();
	map->height = img->GetHeight();
	map->depth = 0;

	// 저장
	_lock_mip = mipno;
	_lock_sfn = sliceface;

	return true;
}

//
bool GLTexture::Unmap()
{
	k_return_value_if_fail(_lock_mip >= 0, false);

	// 그냥? 아님 밉맵?
	qgImage* img = _img_mip ? _img_mip : _img_rsw;
	k_return_value_if_fail(img, false);

	// 올림
	_UploadTexture(false, _lock_mip, (GLRdh*)qgRdh::Instance);

	// 지우라면 지우지요
	if (_img_mip)
	{
		_img_mip->Unload();
		_img_mip = NULL;
	}
	else if (!K_OFLAG(_flags, QGTEXF_IMAGE))
	{
		_img_rsw->Unload();
		_img_rsw = NULL;
	}

	// 정리
	_lock_mip = -1;
	_lock_sfn = QGTXFX_NONE;

	return true;
}


//////////////////////////////////////////////////////////////////////////
// 모션 텍스쳐
KIMPL_SUBCLASS(GLTextureMotion, "GLTextureMotion");

//
GLTextureMotion::GLTextureMotion() throw()
{
}

//
GLTextureMotion::~GLTextureMotion() throw()
{
	// 렌더러 마다 텍스쳐 해제가 다르므로 여기서 함
	if (_mt_preimgs.empty())
		_ClearStreams();
	else
	{
		kForEach(kuintptr n, _mt_preimgs)
		{
			GLuint tex = (GLuint)n;
			glDeleteTextures(1, &tex);
		}
	}
}

//
bool GLTextureMotion::MotionUpdate(float adv)
{
	k_return_value_if_fail(_lock_mip < 0, false);

	//
	kuintptr ptr;
	bool res;

	if (!ManualMotionUpdate(adv, res, ptr))
		return false;

	if (res)
	{
		GLRdh* rdh = (GLRdh*)qgRdh::Instance;
		rdh->CheckBindTexture(0, GL_TEXTURE_2D, (GLuint)_node_desc, 0, _mips);

		// 실제 텍스쳐 사이즈랑 다르면 다르게 처리
		// 반드시 _gl_Type==GL_TEXTURE_2D
		qgImage* img;

		if (_img_rsw)
		{
			// 이미지를 늘린다
			// 짱느림!!!!
			img = _img_rsw;

			qgBlitImage::StrechBlit(QG_BLITOP_TEX, img, NULL, _mt_ucmimg, NULL, tpl::ColorValues::Transparent);

			glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
				img->GetWidth(), img->GetHeight(), _pixel_format, _pixel_type, img->GetData());
		}
		else
		{
			// 무조건 32비트 RGBA만 사용함
			img = _mt_ucmimg;

			glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
				img->GetWidth(), img->GetHeight(), _pixel_format, _pixel_type, img->GetData());
		}
	}
	else if (ptr != 0)
	{
		if (ptr != _node_desc)
		{
			// 헐... 참조가 엄꾸나
			_node_desc = ptr;
		}
	}

	return true;
}

//
bool GLTextureMotion::_CreateMotion(const char* filename, kint flags, GLRdh* rdh)
{
	// 스트림
	kStream* stm = rdh->GetStub()->FileOpen(filename, true);
	if (stm == NULL)
		return NULL;

	if (!qgFwSupp::CheckHeadAndTail(stm))
	{
		stm->Unload();
		return NULL;
	}

	// 모션
	if (!_ParseMotion(stm, K_OFLAG(flags, QGTEXF_ALLOC)))
		return false;

	// 여기에서 쓴 색깔 포맷은 윈도우8 전용이다!!! 
	// DXGI 1.1 -> 1.0으로 하려면 RGBA를 BGRA로 고쳐야함
	qgImage* img = _mt_ucmimg;

	if (_mt_preimgs.empty())
	{
		// 한장 갖다 잠궈 씀
		if (!_InitProperty(filename, QGTEXTURE_2D, img->GetWidth(), img->GetHeight(), 1, 1, QGTEXF_NOMIP | QGRESF_DYNAMIC | QGTEXF_MOTION, QGCF32_RGBA, rdh) ||
			!_CreateTexture(NULL, rdh))
			return false;

		// 실제 텍스쳐 사이즈랑 다르면 버퍼를 다시 만듦 -> 잠기기 용
		if (_insts.adjpow2 || _insts.adjsqr)
			_img_rsw = kobj_new(qgImage, _gl_width, _gl_height, _fmt);
	}
	else
	{
		// 모두 만들어 놓기
		if (!_InitProperty(filename, QGTEXTURE_2D, img->GetWidth(), img->GetHeight(), 1, 1, QGTEXF_NOMIP | QGTEXF_MOTION, QGCF32_RGBA, rdh))
			return false;

		// 실제 텍스쳐 사이즈랑 다르면 버퍼를 다시 만들어야함 -> 잠그기용
		qgImage* imgexp;

		if (!_insts.adjpow2 && !_insts.adjsqr)
			imgexp = NULL;
		else
		{
			_img_rsw = kobj_new(qgImage, _gl_width, _gl_height, _fmt);
			imgexp = _img_rsw;
		}

		//
		GLuint tex;

		for (kuint i = 0; i < _mt_count; i++)
		{
			if (!_CreateInternal(&tex, rdh))
			{
				// 오류 난 프레임만 패스
				tex = 0;
			}
			else
			{
				// 이미지를 만들어 써 넣는다
				if (_UpdateStreamImage(i))
				{
					if (imgexp)
					{
						// 이미지 늘려서 그림
						qgBlitImage::StrechBlit(QG_BLITOP_TEX, imgexp, NULL, img, NULL, tpl::ColorValues::Transparent);

						glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
							imgexp->GetWidth(), imgexp->GetHeight(), _pixel_format, _pixel_type, imgexp->GetData());
					}
					else
					{
						// 무조건 32비트 RGBA만 사용함
						glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
							img->GetWidth(), img->GetHeight(), _pixel_format, _pixel_type, img->GetData());
					}
				}
			}

			_mt_preimgs[i] = (kuintptr)tex;
		}

		// 0번 넣어 둠
		_node_desc = _mt_preimgs[0];

		// 정리
		_ClearStreams();

		_insts.list = true;
	}

	// 오케
	return true;
}


//////////////////////////////////////////////////////////////////////////
// 오프 텍스쳐
KIMPL_SUBCLASS(GLTextureOff, "GLTextureOff");

//
GLTextureOff::GLTextureOff() throw()
{
}

//
GLTextureOff::~GLTextureOff() throw()
{
	if (_fbo != 0)
		glDeleteFramebuffers(1, &_fbo);

	kobj_unload(_db);
}

//
bool GLTextureOff::_CreateOff(GLRdh* rdh)
{
	GLuint fbo = 0;

	// 프레임버퍼
	glGenFramebuffers(1, &fbo);
	if (fbo == 0)
		return false;

	glBindFramebuffer(GL_FRAMEBUFFER, fbo);
	//glDrawBuffer(GL_COLOR_ATTACHMENT0);

	// 프레임버퍼에 텍스쳐 붙임
	rdh->CheckBindTexture(0, _gl_type, (GLuint)_node_desc, 0, 0);

	switch (K_CAST_ENUM(_type))
	{
		case QGTEXTURE_2D:
			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, (GLuint)_node_desc, 0);
			break;

		case QGTEXTURE_3D:		// 미구현
		case QGTEXTURE_CUBE:	// 미구현
		default:
			glBindFramebuffer(GL_FRAMEBUFFER, 0);
			glDeleteFramebuffers(1, &fbo);
			return false;
	}

	// 검사
	const char* chk = CheckFBO();

	glBindFramebuffer(GL_FRAMEBUFFER, 0);

	if (chk != NULL)
	{
		kobj_tracet(KLV_ERROR, KERR_INVAL, chk);

		glDeleteFramebuffers(1, &fbo);

		return false;
	}

	// 오케
	_fbo = fbo;

	return true;
}

//
const char* GLTextureOff::CheckFBO()
{
	GLenum s = glCheckFramebufferStatus(GL_FRAMEBUFFER);
	const char* msg;

	switch (s)
	{
		case GL_FRAMEBUFFER_COMPLETE:
			msg = NULL;
			break;

		case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
			msg = "incomplete image attachments";
			break;

		case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
			msg = "missing image";
			break;

		case GL_FRAMEBUFFER_UNSUPPORTED:
			msg = "unsupported format";
			break;

		default:
			msg = "unknown error";
			break;
	}

	return msg;
}

//
bool GLTextureOff::Enter(kint clearance, const tpl::ColorValue& color, float depth, kuint stencil)
{
	if (_insts.lockoff)
		return false;

	GLRdh* rdh = (GLRdh*)qgRdh::Instance;

	// 깊이 버퍼
	if (_db == NULL)
	{
		_db = (GLDepthBuffer*)rdh->CreateDepthBuffer(_gl_width, _gl_height);

		if (_db != NULL && !_db->Attach(this))
		{
			_db->Unload();
			_db = NULL;
		}
	}
	else
	{
		if (_db->GetLastAttach() != this && !_db->Attach(this))
		{
			_db->Unload();
			_db = NULL;
		}
	}

	// 설정
	rdh->SetTexture(0, NULL);
	rdh->CheckBindTexture(0, _gl_type, (GLuint)_node_desc, 0, 0);

	qgTexture* rts = this;
	rdh->SetRenderTargets(1, &rts);

	rdh->ClearMRTs(clearance, 1, &color, depth, stencil);

	// 올ㅋ
	_insts.lockoff = true;

	return true;
}

bool GLTextureOff::Leave()
{
	if (!_insts.lockoff)
		return false;

	GLRdh* rdh = (GLRdh*)qgRdh::Instance;

	//
	glBindFramebuffer(GL_FRAMEBUFFER, 0);

	//
	rdh->SetRenderTargets(0, NULL);
	rdh->SetTexture(0, NULL);

	_insts.lockoff = false;

	return true;
}

#endif	// _QG_USE_GL
